#include "Frustum.h"
#include "Engine.h"
#include "LineDrawer.h"

Frustum::Frustum() : 
m_nearD(0.0f), 
    m_farD(0.0f), 
    m_ratio(0.0f), 
    m_angle(0.0f), 
    m_tang(0.0f),
    m_nw(0.0f), 
    m_nh(0.0f), 
    m_fw(0.0f), 
    m_fh(0.0f)
{}

static float PIOVER180 = (float)M_PI / 180.0f;
void Frustum::SetProjectionInternalsPerspective(float angle, float ratio, float nearD, float farD)
{
    m_ratio = ratio;
    m_angle = angle;
    m_nearD = nearD;
    m_farD = farD;

    m_tang = (float)tan((angle) * 0.5f);
    m_nh = nearD * m_tang;
    m_nw = m_nh * ratio;
    m_fh = farD * m_tang;
    m_fw = m_fh * ratio;
}
void Frustum::SetProjectionInternalsOrthographic( float width, float height, float nearD, float farD )
{
    m_nearD = nearD;
    m_farD = farD;

    m_nh = height/2;
    m_nw = width/2;
    m_fh =m_nh;
    m_fw = m_nw;
}
void Frustum::SetViewInternals(const Vec3& pos, const Vec3& look, const Vec3& up)
{
    //Vectors for the direction, near clip, flar clip and X,Y,Z axis
    Vec3 dir, nc, fc, x, y, z;

    //Work out the axis of orientation based on position and where we are looking
    z = look - pos;
    z.normalise();

    x = up.CrossProduct(z);
    x.normalise();

    y = z.CrossProduct(x);

    //Near clip is located at
    nc = pos + z * m_nearD;

    //far clip is located at
    fc = pos + z * m_farD;

    //Store the 8 points of the frustum shape

    //near stuff first
    m_ntl = nc + (y * m_nh) - (x * m_nw);
    m_ntr = nc + (y * m_nh) + (x * m_nw);
    m_nbl = nc - (y * m_nh) - (x * m_nw);
    m_nbr = nc - (y * m_nh) + (x * m_nw);

    //far clip stuff now
    m_ftl = fc + (y * m_fh) - (x * m_fw);
    m_ftr = fc + (y * m_fh) + (x * m_fw);
    m_fbl = fc - (y * m_fh) - (x * m_fw);
    m_fbr = fc - (y * m_fh) + (x * m_fw);

    m_planes[TOP].CreateFromPoints(m_ntl,m_ntr , m_ftl);
    m_planes[BOTTOM].CreateFromPoints(m_nbl,m_fbr , m_nbr);
    m_planes[LEFT].CreateFromPoints(m_ntl,m_ftl ,m_nbl );
    m_planes[RIGHT].CreateFromPoints(m_nbr,m_fbr,m_ntr);
    m_planes[NEARP].CreateFromPoints(m_ntl, m_nbl,m_ntr );
    m_planes[FARP].CreateFromPoints(m_ftr, m_fbl,m_ftl );
}
int Frustum::IsPointInFrustum(const Vec3& q)
{
    for(int i = 0; i < 6; ++i)
    {
        if(m_planes[i].Distance(q) < 0)
            return OUTSIDE; //0
    }
    return INSIDE; //1
}
int Frustum::IsSphereInFrustum(const Vec3& q, const float& rad) const
{
    for(int i = 0; i < 6; ++i)
    {
        float distance = 0.0f;
        distance = m_planes[i].Distance(q);
        if(distance < - rad)
            return OUTSIDE;//0
        else if(distance == rad)
            return ON; //2
    }
    return INSIDE; //1
}
void Frustum::Draw() const
{
    //near plane
    Engine::Instance()->GetLineDrawer()->drawLine(m_ntl.m_x,m_ntl.m_y,m_ntl.m_z,
                               m_ntr.m_x,m_ntr.m_y,m_ntr.m_z,
                               1,1,1);
    Engine::Instance()->GetLineDrawer()->drawLine(m_ntr.m_x,m_ntr.m_y,m_ntr.m_z,
                                m_nbr.m_x,m_nbr.m_y,m_nbr.m_z,
                                1,1,1);
    Engine::Instance()->GetLineDrawer()->drawLine(m_nbr.m_x,m_nbr.m_y,m_nbr.m_z,
                                m_nbl.m_x,m_nbl.m_y,m_nbl.m_z,
                                1,1,1);
    Engine::Instance()->GetLineDrawer()->drawLine(m_nbl.m_x,m_nbl.m_y,m_nbl.m_z,
                                m_ntl.m_x,m_ntl.m_y,m_ntl.m_z,
                                1,1,1);

    //far plane
    Engine::Instance()->GetLineDrawer()->drawLine(m_ftr.m_x,m_ftr.m_y,m_ftr.m_z,
                                m_ftl.m_x,m_ftl.m_y,m_ftl.m_z,
                                1,1,1);
    Engine::Instance()->GetLineDrawer()->drawLine(m_ftl.m_x,m_ftl.m_y,m_ftl.m_z,
                                m_fbl.m_x,m_fbl.m_y,m_fbl.m_z,
                                1,1,1);
    Engine::Instance()->GetLineDrawer()->drawLine(m_fbl.m_x,m_fbl.m_y,m_fbl.m_z,
                                m_fbr.m_x,m_fbr.m_y,m_fbr.m_z,
                                1,1,1);
    Engine::Instance()->GetLineDrawer()->drawLine(m_fbr.m_x,m_fbr.m_y,m_fbr.m_z,
                                m_ftr.m_x,m_ftr.m_y,m_ftr.m_z,
                                1,1,1);

    //adjoining lines
    Engine::Instance()->GetLineDrawer()->drawLine(m_ntl.m_x,m_ntl.m_y,m_ntl.m_z,
                                m_ftl.m_x,m_ftl.m_y,m_ftl.m_z,
                                1,1,1);
    Engine::Instance()->GetLineDrawer()->drawLine(m_nbl.m_x,m_nbl.m_y,m_nbl.m_z,
                                m_fbl.m_x,m_fbl.m_y,m_fbl.m_z,
                                1,1,1);
    Engine::Instance()->GetLineDrawer()->drawLine(m_nbr.m_x,m_nbr.m_y,m_nbr.m_z,
                                m_fbr.m_x,m_fbr.m_y,m_fbr.m_z,
                                1,1,1);
    Engine::Instance()->GetLineDrawer()->drawLine(m_ntr.m_x,m_ntr.m_y,m_ntr.m_z,
                                m_ftr.m_x,m_ftr.m_y,m_ftr.m_z,
                                1,1,1);
}
int Frustum::IsBoxInFrustum( D3DXMATRIXA16& transform, D3DXVECTOR3& min,D3DXVECTOR3& max )const
{
    //not done yet
    assert(0);
    D3DXVECTOR3 points[8];
    float sizeX = max.x - min.x;
    float sizeY = max.y - min.y;
    float sizeZ = max.z - min.z;
    points[0] = points[1] = points[2] = points[3] = min;
    points[1].x += sizeX; 
    points[2].y += sizeY;
    points[3].x += sizeX; points[2].y += sizeY;
    points[4] = points[5] = points[6] = points[7] = max;
    points[5].x -= sizeX; 
    points[6].y -= sizeY;
    points[7].x -= sizeX; points[2].y -= sizeY;
    D3DXVECTOR4 transformPoints[8];
    for (int p = 0; p < 8; ++p)
    {
        D3DXVec3Transform(&transformPoints[p], &points[p], &transform);
    }
    return -1;
}
